package lsp;

import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;

import api.LspParams;

public class LspConnection {
	private DatagramSocket ds;
	private LinkedBlockingDeque<LspMessage> saida;
	private AtomicBoolean epochExpired = new AtomicBoolean(false);
	private AtomicBoolean epochLimitExpired = new AtomicBoolean(false);
	private short connId;
	private LspParams params;
	private InetAddress client;
	private int port;
	private short seqNumber = 0;
	private LspMessage lastReceivedAck = null;
	private LspMessage lastReceivedMessage = null;

	public InetAddress getClient() {
		return client;
	}

	public int getPort() {
		return port;
	}

	public AtomicBoolean getEpochLimitExpired() {
		return epochLimitExpired;
	}

	public LspConnection(DatagramSocket ds, short connId, LspParams params,
			InetAddress client, int port) {
		this.ds = ds;
		this.connId = connId;
		this.params = params;
		this.client = client;
		this.port = port;
		this.saida = new LinkedBlockingDeque<LspMessage>();
		Thread epochTrigger = new Thread(new LspConnectionEpochTrigger(
				this.params, this.epochExpired, this.epochLimitExpired));
		Thread outPackageHandler = new Thread(
				new LspConnectionOutPackageHandler(this.ds, this.saida,
						this.epochExpired, this.client, this.connId, this.port));
		epochTrigger.start();
		outPackageHandler.start();
	}

	public void close() {
		// Fazer atividades antes de fechar
		// this.epochLimitExpired.set(true);
		Thread.currentThread().interrupt();
	}

	public void enviaData(byte[] payload) {
		LspMessage mensagem = new LspMessage((short) 1, this.connId,
				nextSeqNumber(), payload);
		this.saida.addFirst(mensagem);
	}

	private short nextSeqNumber() {
		if (this.seqNumber == (Short.MAX_VALUE - Short.MIN_VALUE))
			this.seqNumber = 0;
		else
			this.seqNumber = (short) (this.seqNumber + 1);
		return this.seqNumber;
	}

	public void recebeAck(LspMessage mensagemRecebida) throws InterruptedException {
		// TODO Auto-generated method stub
		if (this.lastReceivedAck == null
				|| mensagemRecebida.getSeqNumber() == (this.lastReceivedAck
						.getSeqNumber() + 1)) {
			this.lastReceivedAck = mensagemRecebida;
			if (!this.saida.isEmpty()
					&& mensagemRecebida.getSeqNumber() == this.saida.peekLast()
							.getSeqNumber())
				this.saida.takeLast();
		}
	}

	public void enviaAck(LspMessage mensagemRecebida) throws InterruptedException {
		// TODO Auto-generated method stub
		this.saida.putLast(mensagemRecebida);
	}

	public boolean recebeData(LspMessage mensagemRecebida) throws InterruptedException {
		// TODO Auto-generated method stub
		if (this.lastReceivedMessage == null) {
			this.lastReceivedMessage = mensagemRecebida;
			this.enviaAck(new LspMessage((short) 2, mensagemRecebida
					.getConnId(), mensagemRecebida.getSeqNumber(), ""
					.getBytes()));
			return true;
		} else if (mensagemRecebida.getSeqNumber() == (this.lastReceivedMessage
				.getSeqNumber() + 1)) {
			this.lastReceivedMessage = mensagemRecebida;
			return true;
		}
		return false;
	}

}
